/******************************************************************************
 *
 *  Copyright (C) 2010-2014 Broadcom Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/

/******************************************************************************
 *
 *  The original Work has been changed by NXP Semiconductors.
 *
 *  Copyright (C) 2015 NXP Semiconductors
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/

/******************************************************************************
 *
 *  Entry point for NFC_TASK
 *
 ******************************************************************************/
#include <string.h>

#include <android-base/stringprintf.h>
#include <base/logging.h>

#include "bt_types.h"
#include "gki.h"
#include "nci_hmsgs.h"
#include "nfc_int.h"
#include "nfc_target.h"

#include "nfa_dm_int.h"

using android::base::StringPrintf;

extern bool nfc_debug_enabled;

/*******************************************************************************
**
** Function         nfc_start_timer
**
** Description      Start a timer for the specified amount of time.
**                  NOTE: The timeout resolution is in SECONDS! (Even
**                          though the timer structure field is ticks)
**
** Returns          void
**
*******************************************************************************/
void nfc_start_timer(TIMER_LIST_ENT *p_tle, uint16_t type, uint32_t timeout) {
  NFC_HDR *p_msg;

  /* if timer list is currently empty, start periodic GKI timer */
  if (nfc_cb.timer_queue.p_first == NULL) {
    /* if timer starts on other than NFC task (scritp wrapper) */
    if (GKI_get_taskid() != NFC_TASK) {
      /* post event to start timer in NFC task */
      p_msg = (NFC_HDR *)GKI_getbuf(NFC_HDR_SIZE);
      if (p_msg != NULL) {
        p_msg->event = BT_EVT_TO_START_TIMER;
        GKI_send_msg(NFC_TASK, NFC_MBOX_ID, p_msg);
      }
    } else {
      /* Start nfc_task 1-sec resolution timer */
      GKI_start_timer(NFC_TIMER_ID, GKI_SECS_TO_TICKS(1), true);
    }
  }

  GKI_remove_from_timer_list(&nfc_cb.timer_queue, p_tle);

  p_tle->event = type;
  p_tle->ticks = timeout; /* Save the number of seconds for the timer */

  GKI_add_to_timer_list(&nfc_cb.timer_queue, p_tle);
}

/*******************************************************************************
**
** Function         nfc_remaining_time
**
** Description      Return amount of time to expire
**
** Returns          time in second
**
*******************************************************************************/
uint32_t nfc_remaining_time(TIMER_LIST_ENT *p_tle) {
  return (GKI_get_remaining_ticks(&nfc_cb.timer_queue, p_tle));
}

/*******************************************************************************
**
** Function         nfc_process_timer_evt
**
** Description      Process nfc GKI timer event
**
** Returns          void
**
*******************************************************************************/
void nfc_process_timer_evt(void) {
  TIMER_LIST_ENT *p_tle;

  GKI_update_timer_list(&nfc_cb.timer_queue, 1);

  while ((nfc_cb.timer_queue.p_first) && (!nfc_cb.timer_queue.p_first->ticks)) {
    p_tle = nfc_cb.timer_queue.p_first;
    GKI_remove_from_timer_list(&nfc_cb.timer_queue, p_tle);
    switch (p_tle->event) {
    case NFC_TTYPE_NCI_WAIT_RSP:
      nfc_ncif_cmd_timeout();
      break;
    case NFC_TTYPE_WAIT_MODE_SET_NTF:
      nfc_modeset_ntf_timeout();
      break;

    default:
      DLOG_IF(INFO, nfc_debug_enabled)
          << StringPrintf("nfc_process_timer_evt: timer:0x%p event (0x%04x)",
                          p_tle, p_tle->event);
      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
          "nfc_process_timer_evt: unhandled timer event (0x%04x)",
          p_tle->event);
    }
  }

  /* if timer list is empty stop periodic GKI timer */
  if (nfc_cb.timer_queue.p_first == NULL) {
    GKI_stop_timer(NFC_TIMER_ID);
  }
}

/*******************************************************************************
**
** Function         nfc_stop_timer
**
** Description      Stop a timer.
**
** Returns          void
**
*******************************************************************************/
void nfc_stop_timer(TIMER_LIST_ENT *p_tle) {
  GKI_remove_from_timer_list(&nfc_cb.timer_queue, p_tle);

  /* if timer list is empty stop periodic GKI timer */
  if (nfc_cb.timer_queue.p_first == NULL) {
    GKI_stop_timer(NFC_TIMER_ID);
  }
}

/*******************************************************************************
**
** Function         nfc_start_quick_timer
**
** Description      Start a timer for the specified amount of time.
**                  NOTE: The timeout resolution depends on including modules.
**                  QUICK_TIMER_TICKS_PER_SEC should be used to convert from
**                  time to ticks.
**
**
** Returns          void
**
*******************************************************************************/
void nfc_start_quick_timer(TIMER_LIST_ENT *p_tle, uint16_t type,
                           uint32_t timeout) {
  NFC_HDR *p_msg;

  /* if timer list is currently empty, start periodic GKI timer */
  if (nfc_cb.quick_timer_queue.p_first == NULL) {
    /* if timer starts on other than NFC task (scritp wrapper) */
    if (GKI_get_taskid() != NFC_TASK) {
      /* post event to start timer in NFC task */
      p_msg = (NFC_HDR *)GKI_getbuf(NFC_HDR_SIZE);
      if (p_msg != NULL) {
        p_msg->event = BT_EVT_TO_START_QUICK_TIMER;
        GKI_send_msg(NFC_TASK, NFC_MBOX_ID, p_msg);
      }
    } else {
      /* Quick-timer is required for LLCP */
      GKI_start_timer(NFC_QUICK_TIMER_ID,
                      ((GKI_SECS_TO_TICKS(1) / QUICK_TIMER_TICKS_PER_SEC)),
                      true);
    }
  }

  GKI_remove_from_timer_list(&nfc_cb.quick_timer_queue, p_tle);

  p_tle->event = type;
  p_tle->ticks = timeout; /* Save the number of ticks for the timer */

  GKI_add_to_timer_list(&nfc_cb.quick_timer_queue, p_tle);
}

/*******************************************************************************
**
** Function         nfc_stop_quick_timer
**
** Description      Stop a timer.
**
** Returns          void
**
*******************************************************************************/
void nfc_stop_quick_timer(TIMER_LIST_ENT *p_tle) {
  GKI_remove_from_timer_list(&nfc_cb.quick_timer_queue, p_tle);

  /* if timer list is empty stop periodic GKI timer */
  if (nfc_cb.quick_timer_queue.p_first == NULL) {
    GKI_stop_timer(NFC_QUICK_TIMER_ID);
  }
}

/*******************************************************************************
**
** Function         nfc_process_quick_timer_evt
**
** Description      Process quick timer event
**
** Returns          void
**
*******************************************************************************/
void nfc_process_quick_timer_evt(void) {
  TIMER_LIST_ENT *p_tle;

  GKI_update_timer_list(&nfc_cb.quick_timer_queue, 1);

  while ((nfc_cb.quick_timer_queue.p_first) &&
         (!nfc_cb.quick_timer_queue.p_first->ticks)) {
    p_tle = nfc_cb.quick_timer_queue.p_first;
    GKI_remove_from_timer_list(&nfc_cb.quick_timer_queue, p_tle);

    switch (p_tle->event) {
    default:
      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
          "nfc_process_quick_timer_evt: unhandled timer event (0x%04x)",
          p_tle->event);
      break;
    }
  }

  /* if timer list is empty stop periodic GKI timer */
  if (nfc_cb.quick_timer_queue.p_first == NULL) {
    GKI_stop_timer(NFC_QUICK_TIMER_ID);
  }
}

/*******************************************************************************
**
** Function         nfc_task_shutdown_nfcc
**
** Description      Handle NFC shutdown
**
** Returns          nothing
**
*******************************************************************************/
void nfc_task_shutdown_nfcc(void) {
  NFC_HDR *p_msg;

  /* Free any messages still in the mbox */
  while ((p_msg = (NFC_HDR *)GKI_read_mbox(NFC_MBOX_ID)) != NULL) {
    GKI_freebuf(p_msg);
  }

  nfc_gen_cleanup();

  if (nfc_cb.flags & NFC_FL_POWER_OFF_SLEEP) {
    nfc_set_state(NFC_STATE_W4_HAL_CLOSE);
    nfc_cb.p_hal->close();
  } else if (nfc_cb.flags & NFC_FL_POWER_CYCLE_NFCC) {
    nfc_set_state(NFC_STATE_W4_HAL_OPEN);
    nfc_cb.p_hal->power_cycle();
  } else {
    nfc_set_state(NFC_STATE_W4_HAL_CLOSE);
    nfc_cb.p_hal->close();

    /* Stop the timers */
    GKI_stop_timer(NFC_TIMER_ID);
    GKI_stop_timer(NFC_QUICK_TIMER_ID);
    GKI_stop_timer(NFA_TIMER_ID);
  }
}

/*******************************************************************************
**
** Function         nfc_task
**
** Description      NFC event processing task
**
** Returns          nothing
**
*******************************************************************************/
uint32_t nfc_task(__attribute__((unused)) uint32_t arg) {
  uint16_t event;
  NFC_HDR *p_msg;
  bool free_buf;

  /* Initialize the nfc control block */
  memset(&nfc_cb, 0, sizeof(tNFC_CB));

  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("NFC_TASK started.");

  /* main loop */
  while (true) {
    event = GKI_wait(0xFFFF, 0);
    if (event == EVENT_MASK(GKI_UNKNOWN_TASK_EVT)) {
      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("GKI_UNKNOWN_TASK_EVT");
      break;
    } else if (event == EVENT_MASK(GKI_SHUTDOWN_EVT)) {
      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("GKI_SHUTDOWN_EVT");
      break;
    }
    /* Handle NFC_TASK_EVT_TRANSPORT_READY from NFC HAL */
    if (event & NFC_TASK_EVT_TRANSPORT_READY) {
      DLOG_IF(INFO, nfc_debug_enabled)
          << StringPrintf("NFC_TASK got NFC_TASK_EVT_TRANSPORT_READY.");
      check_nfcee_session_and_reset();
      nfc_set_state(NFC_STATE_CORE_INIT);
      /* Reset the NFC controller. */
      //check_nfcee_session_and_reset();
      nci_snd_core_reset(NCI_RESET_TYPE_KEEP_CFG);
    }

    if (event & NFC_MBOX_EVT_MASK) {
      /* Process all incoming NCI messages */
      while ((p_msg = (NFC_HDR *)GKI_read_mbox(NFC_MBOX_ID)) != NULL) {
        free_buf = true;

        /* Determine the input message type. */
        switch (p_msg->event & NFC_EVT_MASK) {
        case BT_EVT_TO_NFC_NCI:
          free_buf = nfc_ncif_process_event(p_msg);
          break;

        case BT_EVT_TO_START_TIMER:
          /* Start nfc_task 1-sec resolution timer */
          GKI_start_timer(NFC_TIMER_ID, GKI_SECS_TO_TICKS(1), true);
          break;

        case BT_EVT_TO_START_QUICK_TIMER:
          /* Quick-timer is required for LLCP */
          GKI_start_timer(NFC_QUICK_TIMER_ID,
                          ((GKI_SECS_TO_TICKS(1) / QUICK_TIMER_TICKS_PER_SEC)),
                          true);
          break;

        case BT_EVT_TO_NFC_MSGS:
          nfc_main_handle_hal_evt((tNFC_HAL_EVT_MSG *)p_msg);
          break;

        default:
          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
              "nfc_task: unhandle mbox message, event=%04x", p_msg->event);
          break;
        }

        if (free_buf) {
          GKI_freebuf(p_msg);
        }
      }
    }

    /* Process gki timer tick */
    if (event & NFC_TIMER_EVT_MASK) {
      nfc_process_timer_evt();
    }

    /* Process quick timer tick */
    if (event & NFC_QUICK_TIMER_EVT_MASK) {
      nfc_process_quick_timer_evt();
    }

    if (event & NFA_MBOX_EVT_MASK) {
      while ((p_msg = (NFC_HDR *)GKI_read_mbox(NFA_MBOX_ID)) != NULL) {
        nfa_sys_event(p_msg);
      }
    }

    if (event & NFA_TIMER_EVT_MASK) {
      nfa_sys_timer_update();
    }
  }

  DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("nfc_task terminated");
  return 0;
}
